<?php

namespace app\controller\Api\V1\Client\Auth;

use app\helper\AccountBan;
use app\helper\Config;
use app\helper\SmsVerifyCode;
use app\model\AccountSecretProtection;
use app\model\AreaLoginRecord;
use app\model\AutoTaskRecord;
use app\model\User;
use app\request\Account;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Hashing\BcryptHasher;
use Illuminate\Support\Arr;
use Shopwwi\WebmanAuth\Facade\JWT as JwtFace;
use support\Log;
use support\Redis;
use support\Request;
use support\Response;

class AccountController
{

    protected Config $configRe;
    protected SmsVerifyCode $smsVerifyCodeRe;
    protected string $userInfoKeyPrefix;
    protected string $platform = 'game';


    public function __construct()
    {
        $this->configRe = Config::getInstance();
        $this->smsVerifyCodeRe = SmsVerifyCode::getInstance();
        $this->userInfoKeyPrefix = config('redis.prefix') . ':GAME:USER:INFO:';
    }


    /**
     * 注册
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function Register(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('register')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        // 检查账号
        if (User::query()->where('account', $request->input('account'))->exists()) {
            throw new Exception('账号已被注册', 500);
        }

        try {
            $hash = new BcryptHasher();

            $userInfo = User::query()->create([
                'account' => $request->input('account'),
                'nickname' => $request->input('account') ?? GetNickname(),
                'avatar' => $this->configRe->Get('user_default_avatar'),
                'password' => $hash->make(sha1($request->input('password'))),
                'invitation_code' => GetInvitationCode(),
                'registration_source' => $this->platform,
                'registration_client' => null,
                'register_ip' => $request->getRealIp(),
            ]);

            AutoTaskRecord::query()->create([
                'label' => 'user-login-game-operation',
                'params' => json_encode([
                    'scene' => 'register',
                    'user' => $userInfo['ulid'],
                    'request' => $request->all(),
                    'platform' => $this->platform,
                    'ip' => $request->getRealIp(),
                ]),
                'run_time' => time(),
                'state' => 0,
            ]);
        } catch (Exception $e) {
            Log::info('账号注册失败：' . $e->getMessage());
            throw new Exception('账号注册失败', 500);
        }

        return jsonRe([], 200, '注册成功');

    }


    /**
     * 账号登录
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function Login(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('login')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        $postParams = $request->post();
        $postParams['client_type'] = $this->platform;
        $request->setPost($postParams);

        $userInfo = User::query()->where('account', $request->input('account'))->first();
        if (!$userInfo) {
            throw new Exception('账号未注册', 500);
        }
        $userInfo = $userInfo->toArray();
        if ($userInfo['state'] !== 1) {
            throw new Exception('账号已禁用111', 500);
        }

        // 检查密码

        $hash = new BcryptHasher();
        if (!$hash->check(sha1($request->input('password')), $userInfo['password'])) {
            throw new Exception('密码错误', 500);
        }

        // 账号封禁状态检查
        $accountBanCheck = AccountBan::getInstance();
        $accountBanCheck->Check($request->input('game_id'), $userInfo['account']);

        AutoTaskRecord::query()->create([
            'label' => 'user-login-game-operation',
            'params' => json_encode([
                'scene' => 'account-login',
                'user' => $userInfo['ulid'],
                'request' => $request->all(),
                'platform' => $this->platform,
                'ip' => $request->getRealIp(),
            ]),
            'run_time' => time(),
            'state' => 0,
        ]);

        return jsonRe([
            'token' => GenerateLoginToken($userInfo, $request->getRealIp(), $this->platform),
        ]);

    }


    /**
     * 手机登录
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function PhoneLogin(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('phone-login')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        $postParams = $request->post();
        $postParams['client_type'] = $this->platform;
        $request->setPost($postParams);

        $userInfo = User::query()->where('phone', $request->input('phone'))->first();

        // 验证码验证
        $this->smsVerifyCodeRe->VerifyCode($request->input('phone'), $request->input('code'), $this->platform, 'login');

        $isRegister = !$userInfo;

        try {
            if (!$userInfo) {  // 注册登录
                $userInfo = User::query()->create([
                    'account' => GetAccount(),
                    'nickname' => $request->input('account') ?? GetNickname(),
                    'avatar' => $this->configRe->Get('user_default_avatar'),
                    'password' => '',
                    'phone' => $request->input('phone'),
                    'invitation_code' => GetInvitationCode(),
                    'registration_source' => $this->platform,
                    'registration_client' => null,
                    'register_ip' => $request->getRealIp(),
                ])->toArray();
            } else {
                $userInfo = $userInfo->toArray();
            }
        } catch (Exception $e) {
            Log::info('登录失败：' . $e->getMessage());
            throw new Exception('登录失败', 500);
        }

        if ($userInfo['state'] !== 1) {
            throw new Exception('账号已禁用222', 500);
        }

        // 账号封禁状态检查
        $accountBanCheck = AccountBan::getInstance();
        $accountBanCheck->Check($request->input('game_id'), $userInfo['account']);

        AutoTaskRecord::query()->create([
            'label' => 'user-login-game-operation',
            'params' => json_encode([
                'scene' => $isRegister ? 'register' : 'phone-login',
                'user' => $userInfo['ulid'],
                'request' => $request->all(),
                'platform' => $this->platform,
                'ip' => $request->getRealIp(),
            ]),
            'run_time' => time(),
            'state' => 0,
        ]);

        return jsonRe([
            'token' => GenerateLoginToken($userInfo, $request->getRealIp(), $this->platform),
        ]);

    }


    /**
     * 刷新token
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function RefreshAuth(Request $request): Response
    {

        try {
            $accessToken = JwtFace::refresh(config('plugin.shopwwi.auth.app.jwt.access_exp'));
        } catch (Exception $e) {
            Log::info('请重新登录：' . $e->getMessage());
            throw new Exception('请重新登录', 999);
        }

        return jsonRe([
            'access_token' => $accessToken->access_token,
        ]);

    }


    /**
     * 获取用户信息
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function GetInfo(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('get-info')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        try {
            $tokenInfo = JwtFace::verify();
        } catch (Exception $e) {
            Log::info('请先登录：' . $e);
            throw new Exception('请先登录', 999);
        }
        if ($tokenInfo->iss !== config('plugin.shopwwi.auth.app.jwt.iss')) {
            throw new Exception('身份验证令牌无效', 999);
        }
        if ($tokenInfo->guard !== 'user') {
            throw new Exception('身份验证令牌无效', 999);
        }
        if ($tokenInfo->iat >= time()) {
            throw new Exception('身份验证令牌无效', 999);
        }
        if ($tokenInfo->exp <= time()) {
            throw new Exception('身份验证令牌无效', 999);
        }

        $userInfoKey = $this->userInfoKeyPrefix . $tokenInfo->extend->ulid;
        if (!Redis::exists($userInfoKey)) {
            throw new Exception('请先登录', 999);
        }
        $userInfo = unserialize(Redis::get($userInfoKey));

        $releaseType = $request->input('release_type') ?? 1;

        if ($releaseType == 1) {
            // 账号封禁状态检查
            $accountBanCheck = AccountBan::getInstance();
            $accountBanCheck->Check($request->input('game_id'), $userInfo['account']);
        }

        // 新增区服登录记录
        AreaLoginRecord::query()->create([
            'user' => $userInfo['ulid'],
            'release_type' => $releaseType,
            'merchant' => $releaseType == 1 ? $request->input('mch_id') : null,
            'developer' => $releaseType != 1 ? $request->input('open_id') : null,
            'game' => $request->input('game_id'),
            'area' => $request->input('area_id'),
        ]);

        Log::info('获取登录响应用户信息：', [
            $userInfo
        ]);

        return jsonRe([
            'info' => [
                'account' => $userInfo['account'],
                'token' => substr($request->header('authorization'), 7),
                'created_at' => date("Y-m-d H:i:s", strtotime($userInfo['created_at'])),
                'updated_at' => date("Y-m-d H:i:s", strtotime($userInfo['updated_at'])),
            ],
        ]);

    }


    /**
     * 重置密码
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function ResetPassword(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('reset-password')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        $userSql = User::query()->where('account', $request->input('account'));

        // 检查账号
        if (!(clone $userSql)->exists()) {
            throw new Exception('账号未注册', 500);
        }

        $userInfo = (clone $userSql)->first()->toArray();

        $hash = new BcryptHasher();

        if (!$hash->check(sha1($request->input('old_password')), $userInfo['password'])) {
            throw new Exception('密码不正确', 500);
        }

        // 修改密码
        (clone $userSql)->update([
            'password' => $hash->make(sha1($request->input('new_password'))),
        ]);

        return jsonRe();

    }


    /**
     * 重置密保
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function ResetSecretProtection(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('reset-secret-protection')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        $userSql = User::query()->where('account', $request->input('account'));

        // 检查账号
        if (!(clone $userSql)->exists()) {
            throw new Exception('账号未注册', 500);
        }

        $userInfo = (clone $userSql)->first()->toArray();

        $hash = new BcryptHasher();

        if (!$hash->check(sha1($request->input('password')), $userInfo['password'])) {
            throw new Exception('密码不正确', 500);
        }

        $secretProtectionVerify = AccountSecretProtection::query()->where('user', $userInfo['ulid'])->exists();

        if (!$secretProtectionVerify) {
            AccountSecretProtection::query()->create([
                'user' => $userInfo['ulid'],
                'question' => $request->input('new_secret_protection'),
                'answer' => $request->input('new_answer'),
            ]);
        } else {
            $oldVerify = AccountSecretProtection::query()->where('user', $userInfo['ulid'])
                ->where('question', $request->input('old_secret_protection'))
                ->where('answer', $request->input('old_answer'))
                ->exists();

            if (!$oldVerify) {
                throw new Exception('原密保错误', 500);
            }

            AccountSecretProtection::query()->where('user', $userInfo['ulid'])->update([
                'question' => $request->input('new_secret_protection'),
                'answer' => $request->input('new_answer'),
            ]);
        }

        return jsonRe();

    }


    /**
     * 提交实名认证
     *
     * @param Request $request
     * @return Response
     * @throws Exception|GuzzleException
     */
    public function RealNameSubmit(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('real-name-submit')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        try {
            $tokenInfo = JwtFace::verify();
        } catch (Exception $e) {
            Log::info('请先登录：' . $e->getMessage());
            throw new Exception('请先登录', 999);
        }
        if ($tokenInfo->iss !== config('plugin.shopwwi.auth.app.jwt.iss')) {
            throw new Exception('身份验证令牌无效', 999);
        }
        if ($tokenInfo->guard !== 'user') {
            throw new Exception('身份验证令牌无效', 999);
        }
        if ($tokenInfo->iat >= time()) {
            throw new Exception('身份验证令牌无效', 999);
        }
        if ($tokenInfo->exp <= time()) {
            throw new Exception('身份验证令牌无效', 999);
        }
//        if ($tokenInfo->extend->ip !== $request->getRealIp()) {
//            throw new Exception('身份验证令牌无效', 999);
//        }

        $url = 'https://sxidcheck.market.alicloudapi.com/idcard/check';
        $appKey = '1ea8b1eff9a84d2fa5c8bfe47318e7da';

        $param = [
            'idCard' => $request->input('id_number'),
            'name' => $request->input('name'),
        ];

        $http = new Client();

        try {
            $http->post($url . '?' . Arr::query($param), [
                'headers' => [
                    'Authorization' => 'APPCODE ' . $appKey,
                    'Content-Type' => 'application/json',
                ],
                'http_errors' => true,
            ]);
        } catch (Exception) {
            throw new Exception('实名信息不符', 500);
        }

//        if ($data['code'] != 0) {
//            throw new Exception($data['msg'], 500);
//        }
//        if ($data['data']['result'] !== 1) {
//            throw new Exception("实名核查结果不一致", 500);
//        }

        User::query()->where('ulid', $tokenInfo->extend->ulid)->update([
            'name' => $request->input('name'),
            'id_number' => $request->input('id_number'),
        ]);

        return jsonRe();

    }


    /**
     * 绑定手机
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function BindPhone(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('bind-phone')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        $userSql = User::query()->where('account', $request->input('account'));

        // 检查账号
        if (!(clone $userSql)->exists()) {
            throw new Exception('账号未注册', 500);
        }

        $userInfo = (clone $userSql)->first()->toArray();

        // 手机号验证
        if (!empty($userInfo['phone'])) {
            throw new Exception('当前账号已绑定手机', 500);
        }

        // 密保验证
        $secretProtectionExist = AccountSecretProtection::query()
            ->where('user', $userInfo['ulid'])->exists();
        if (!$secretProtectionExist) {
            throw new Exception('密保未设置', 500);
        }

        $secretProtectionVerify = AccountSecretProtection::query()
            ->where('user', $userInfo['ulid'])
            ->where('question', $request->input('secret_protection'))
            ->where('answer', $request->input('answer'))
            ->exists();

        if (!$secretProtectionVerify) {
            throw new Exception('密保错误', 500);
        }

        // 验证码验证
        $this->smsVerifyCodeRe->VerifyCode($request->input('phone'), $request->input('code'), $this->platform, 'bind-phone');

        // 修改手机号
        (clone $userSql)->update([
            'phone' => $request->input('phone'),
        ]);

        return jsonRe();

    }


    /**
     * 修改手机
     *
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function ModifyPhone(Request $request): Response
    {

        $validate = new Account();

        if (!$validate->scene('modify-phone')->check($request->all())) {
            throw new Exception($validate->getError(), 500);
        }

        $userSql = User::query()->where('account', $request->input('account'));

        // 检查账号
        if (!(clone $userSql)->exists()) {
            throw new Exception('账号未注册', 500);
        }

        $userInfo = (clone $userSql)->first()->toArray();

        // 原手机号验证
        if ($userInfo['phone'] != $request->input('old_phone')) {
            throw new Exception('原手机号不正确', 500);
        }

        // 新手机号验证
        if ($request->input('phone') === $request->input('old_phone')) {
            throw new Exception('新手机号与原手机号相同', 500);
        }
        if ($userInfo['phone'] === $request->input('phone')) {
            throw new Exception('新手机号已被当前账户绑定', 500);
        }
        $phoneVerify = User::query()->where('phone', $request->input('phone'))->exists();
        if ($phoneVerify) {
            throw new Exception('新手机号已被其他账号绑定', 500);
        }

        // 原手机号验证码验证
        $this->smsVerifyCodeRe->VerifyCode($request->input('old_phone'), $request->input('old_phone_code'), $this->platform, 'modify-phone');

        // 新手机号验证码验证
        $this->smsVerifyCodeRe->VerifyCode($request->input('phone'), $request->input('code'), $this->platform, 'modify-phone');

        // 修改验证码
        (clone $userSql)->update([
            'phone' => $request->input('phone'),
        ]);

        return jsonRe();

    }

}
